package com.nali.dal.statement.query;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;

import com.nali.dal.dao.general.IGeneralQueryDao;
import com.nali.dal.expression.ExpressionDto;
import com.nali.dal.expression.ExpressionValue;
import com.nali.dal.expression.PropertyExpression;
import com.nali.dal.expression.query.Criteria;

/**
 * InstantSelecter<br>&nbsp;
 * 
 * @author sam Created on 2011-4-10
 */
public class InstantSelecter implements Selecter {//TODO 可以基于preparedQueryDao实现PreparedQuerier
	private static Pattern whereFunPattern=Pattern.compile("(\\w+)\\((.+?)\\s*,\\s*(.+?)\\s*\\)");
	private IGeneralQueryDao queryDao;
	protected String collectionName;
	protected List<PropertyExpression<Criteria>> pcList=new ArrayList<PropertyExpression<Criteria>>();
	protected List<ExpressionValueDesc<Criteria>> conditionList=new ArrayList<ExpressionValueDesc<Criteria>>();
	private TranerConfig tranerCfg;
	
	public InstantSelecter() {
	}

	@PostConstruct
	public void postConstruct() {
		//TODO check
	}

	public void setGroupBy(List<String> columns) {
		for (String col : columns) {
			pcList.add(new PropertyExpression<Criteria>(col, Criteria.groupBy));
		}
	}

	public void setSelect(List<String> columns, ColumnUnitFactory columnUnitFactory) throws IllegalArgumentException {
		List<ColumnUnitConfig> columnUnitList = new ArrayList<ColumnUnitConfig>();
		Set<PropertyExpression<Criteria>> propertyExpressions = new HashSet<PropertyExpression<Criteria>>();
		for (String col : columns) {
			ColumnUnitConfig columnUnitConfig = columnUnitFactory.compile(col);
			columnUnitList.add(columnUnitConfig);
			propertyExpressions.addAll(columnUnitConfig.getColumnCriterias());
		}
		for (PropertyExpression<Criteria> propertyExpression : propertyExpressions) {
			pcList.add(propertyExpression);
		}
		tranerCfg=new TranerConfig(columnUnitList);
	}

	public void setFrom(String tab) {
		if(tab==null) {
			throw new IllegalArgumentException("'from' needed");
		}
		this.collectionName = tab;
	}
	
	public void setWhere(List<String> conds, ColumnUnitFactory columnUnitFactory) throws IllegalArgumentException {
		for (String cond : conds) {
			Matcher matcher = whereFunPattern.matcher(cond);
			if(matcher.matches()) {
				Criteria modifier = tranToCriteria(matcher.group(1));
				String prop = matcher.group(2);
				ColumnUnitConfig columnUnitConfig = columnUnitFactory.compile(matcher.group(3));
				if(columnUnitConfig.getColumns().size()!=0) {
					throw new IllegalArgumentException("cannot get a constant result from:" + matcher.group(3));
				}
				conditionList.add(ExpressionValueDesc.getInstance(prop, modifier, columnUnitConfig));
			} else {
				throw new IllegalArgumentException("not supported syntax:" + cond);
			}
		}
	}

	public void setNativeWhere(List<String> conds) {
		for (String cond : conds) {
			pcList.add(new PropertyExpression<Criteria>(cond, Criteria.where));
		}
	
	}
	
	private Criteria tranToCriteria(String fun) throws IllegalArgumentException {
		try {
			return Criteria.valueOf(fun);
		} catch (Exception e) {
			throw new IllegalArgumentException("not supported function:" + fun);
		}
	}

	@Override
	public List<Map<String, Object>> execute(ExpressionValue<Criteria>... conds) {
		List<ExpressionValue<Criteria>> crList = getCrList(conds);
		List<Map<String, Object>> queryRlts = queryDao.queryForList(collectionName, new ExpressionDto<Criteria>(pcList, crList));
		Traner traner = tranerCfg.getTraner();
		return tranQueryRlts(queryRlts, traner);
	}

	@Override
	public List<Map<String, Object>> execute(ExpressionValue<Criteria>[] conds, int offset, int limit) {
		List<ExpressionValue<Criteria>> crList = getCrList(conds);
		List<Map<String, Object>> queryRlts = queryDao.queryForLimit(collectionName, new ExpressionDto<Criteria>(pcList, crList), offset, limit);
		Traner traner = tranerCfg.getTraner();
		return tranQueryRlts(queryRlts, traner);
	}
	
	@Override
	public long count(ExpressionValue<Criteria>... conds) {
		List<ExpressionValue<Criteria>> crList = getCrList(conds);
		return queryDao.count(collectionName, new ExpressionDto<Criteria>(pcList, crList));
	}
	
	protected List<Map<String, Object>> tranQueryRlts(List<Map<String, Object>> queryRlts, Traner traner) {
		List<Map<String, Object>> rlts = new ArrayList<Map<String,Object>>(queryRlts.size());
		for (Map<String,Object> queryRlt : queryRlts) {
			rlts.add(traner.tran(queryRlt));
		}
		return rlts ;
	}

	private List<ExpressionValue<Criteria>> getCrList(ExpressionValue<Criteria>[] conds) {
		List<ExpressionValue<Criteria>> crList;
		if(conds!=null && conds.length>0) {
			crList = new ArrayList<ExpressionValue<Criteria>>(conditionList.size()+conds.length);
			for (ExpressionValue<Criteria> cv : conds) {
				crList.add(cv);
			}
		} else {
			crList = new ArrayList<ExpressionValue<Criteria>>(conditionList.size());
		}
		for (ExpressionValueDesc<Criteria> cvi : conditionList) {
			crList.add(cvi.getExpressionValue(null));
		}
		return crList;
	}

	public void setQueryDao(IGeneralQueryDao queryDao) {
		this.queryDao = queryDao;
	}

	public void setOrderBy(List<String> columns) {
		for (String col : columns) {
			if(col.charAt(0)=='-') {
				pcList.add(new PropertyExpression<Criteria>(col.substring(1), Criteria.desc));
			} else {
				pcList.add(new PropertyExpression<Criteria>(col, Criteria.asc));
			}
		}
	}

}
